/**
* Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package com.python.pydev.ui.search;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.ITextFileBuffer;
import org.eclipse.core.filebuffers.ITextFileBufferManager;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceProxy;
import org.eclipse.core.resources.IResourceProxyVisitor;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.util.Assert;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.search.internal.ui.SearchPlugin;
import org.eclipse.search.internal.ui.text.FileSearchPage;
import org.eclipse.search.internal.ui.text.FileSearchQuery;
import org.eclipse.search.internal.ui.util.ExceptionHandler;
import org.eclipse.search.ui.NewSearchUI;
import org.eclipse.search.ui.text.AbstractTextSearchResult;
import org.eclipse.search.ui.text.Match;
import org.eclipse.swt.widgets.Item;
import org.eclipse.ui.IWorkbenchSite;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
import org.python.pydev.core.uiutils.AsynchronousProgressMonitorDialog;
/* package */class ReplaceAction2 extends Action {
private IWorkbenchSite fSite;
private IFile[] fElements;
private FileSearchPage fPage;
private static class ItemIterator implements Iterator {
private Item[] fArray;
private int fNextPosition;
ItemIterator(Item[] array) {
fArray = array;
fNextPosition = 0;
}
public boolean hasNext() {
return fNextPosition < fArray.length;
}
public Object next() {
if (!hasNext())
throw new NoSuchElementException();
return fArray[fNextPosition++].getData();
}
public void remove() {
throw new UnsupportedOperationException();
}
}
public ReplaceAction2(FileSearchPage page, IFile[] elements) {
Assert.isNotNull(page);
fSite = page.getSite();
if (elements != null)
fElements = elements;
else
fElements = new IFile[0];
fPage = page;
setText(SearchMessages.ReplaceAction_label_all);
setEnabled(!(fElements.length == 0));
}
public ReplaceAction2(FileSearchPage page) {
Assert.isNotNull(page);
fSite = page.getSite();
fPage = page;
Item[] items = null;
StructuredViewer viewer = fPage.getViewer();
if (viewer instanceof TreeViewer) {
items = ((TreeViewer) viewer).getTree().getItems();
} else if (viewer instanceof TableViewer) {
items = ((TableViewer) viewer).getTable().getItems();
}
fElements = collectFiles(new ItemIterator(items));
setText(SearchMessages.ReplaceAction_label_all);
setEnabled(!(fElements.length == 0));
}
public ReplaceAction2(FileSearchPage page, IStructuredSelection selection) {
fSite = page.getSite();
fPage = page;
setText(SearchMessages.ReplaceAction_label_selected);
fElements = collectFiles(selection.iterator());
setEnabled(!(fElements.length == 0));
}
private IFile[] collectFiles(Iterator resources) {
final Set files = new HashSet();
final AbstractTextSearchResult result = fPage.getInput();
if (result == null)
return new IFile[0];
while (resources.hasNext()) {
IResource resource = (IResource) resources.next();
try {
resource.accept(new IResourceProxyVisitor() {
public boolean visit(IResourceProxy proxy) throws CoreException {
if (proxy.getType() == IResource.FILE) {
IResource file = proxy.requestResource();
if (result.getMatchCount(file) > 0) {
files.add(file);
}
return false;
}
return true;
}
}, IResource.NONE);
} catch (CoreException e) {
// TODO Don't know yet how to handle this. This is called when we open the context
// menu. A bad time to show a dialog.
SearchPlugin.getDefault().getLog().log(e.getStatus());
}
}
return (IFile[]) files.toArray(new IFile[files.size()]);
}
public void run() {
IWorkspace workspace = ResourcesPlugin.getWorkspace();
ISchedulingRule rule = workspace.getRuleFactory().modifyRule(workspace.getRoot());
try {
Platform.getJobManager().beginRule(rule, null);
if (validateResources((FileSearchQuery) fPage.getInput().getQuery())) {
ReplaceDialog2 dialog = new ReplaceDialog2(fSite.getShell(), fElements, fPage);
dialog.open();
}
} catch (OperationCanceledException e) {
} finally {
Platform.getJobManager().endRule(rule);
}
}
private boolean validateResources(final FileSearchQuery operation) {
IFile[] readOnlyFiles = getReadOnlyFiles();
IStatus status = ResourcesPlugin.getWorkspace().validateEdit(readOnlyFiles, fSite.getShell());
if (!status.isOK()) {
if (status.getSeverity() != IStatus.CANCEL) {
ErrorDialog.openError(fSite.getShell(), SearchMessages.ReplaceAction2_error_validate_title,
SearchMessages.ReplaceAction2_error_validate_message, status);
}
return false;
}
final List outOfDateEntries = new ArrayList();
for (int j = 0; j < fElements.length; j++) {
IFile entry = fElements[j];
Match[] markers = fPage.getDisplayedMatches(entry);
for (int i = 0; i < markers.length; i++) {
if (isOutOfDate((FileMatch) markers[i])) {
outOfDateEntries.add(entry);
break;
}
}
}
final List outOfSyncEntries = new ArrayList();
for (int i = 0; i < fElements.length; i++) {
IFile entry = fElements[i];
if (isOutOfSync(entry)) {
outOfSyncEntries.add(entry);
}
}
if (outOfDateEntries.size() > 0 || outOfSyncEntries.size() > 0) {
if (askForResearch(outOfDateEntries, outOfSyncEntries)) {
ProgressMonitorDialog pmd = new AsynchronousProgressMonitorDialog(fSite.getShell());
try {
pmd.run(true, true, new WorkspaceModifyOperation(null) {
protected void execute(IProgressMonitor monitor) throws CoreException {
research(monitor, outOfDateEntries, operation);
}
});
return true;
} catch (InvocationTargetException e) {
ExceptionHandler.handle(e, fSite.getShell(), SearchMessages.ReplaceAction_label,
SearchMessages.ReplaceAction_research_error);
} catch (InterruptedException e) {
// canceled
}
}
return false;
}
return true;
}
private IFile[] getReadOnlyFiles() {
Set readOnly = new HashSet();
for (int i = 0; i < fElements.length; i++) {
if (fElements[i].isReadOnly())
readOnly.add(fElements[i]);
}
IFile[] readOnlyArray = new IFile[readOnly.size()];
return (IFile[]) readOnly.toArray(readOnlyArray);
}
private void research(IProgressMonitor monitor, List outOfDateEntries, FileSearchQuery operation)
throws CoreException {
String message = SearchMessages.ReplaceAction2_statusMessage;
MultiStatus multiStatus = new MultiStatus(NewSearchUI.PLUGIN_ID, IStatus.OK, message, null);
for (Iterator elements = outOfDateEntries.iterator(); elements.hasNext();) {
IFile entry = (IFile) elements.next();
IStatus status = research(operation, monitor, entry);
if (status != null && !status.isOK()) {
multiStatus.add(status);
}
}
if (!multiStatus.isOK()) {
throw new CoreException(multiStatus);
}
}
private boolean askForResearch(List outOfDateEntries, List outOfSyncEntries) {
SearchAgainConfirmationDialog dialog = new SearchAgainConfirmationDialog(fSite.getShell(),
(ILabelProvider) fPage.getViewer().getLabelProvider(), outOfSyncEntries, outOfDateEntries);
return dialog.open() == IDialogConstants.OK_ID;
}
private boolean isOutOfDate(FileMatch match) {
if (match.getCreationTimeStamp() != match.getFile().getModificationStamp())
return true;
ITextFileBufferManager bm = FileBuffers.getTextFileBufferManager();
ITextFileBuffer fb = bm.getTextFileBuffer(match.getFile().getFullPath());
if (fb != null && fb.isDirty())
return true;
return false;
}
private boolean isOutOfSync(IFile entry) {
return !entry.isSynchronized(IResource.DEPTH_ZERO);
}
private IStatus research(FileSearchQuery operation, final IProgressMonitor monitor, IFile entry) {
Match[] matches = fPage.getDisplayedMatches(entry);
IStatus status = operation.searchInFile(getResult(), monitor, entry);
// always remove old matches
for (int i = 0; i < matches.length; i++) {
getResult().removeMatch(matches[i]);
}
return status;
}
private AbstractTextSearchResult getResult() {
return fPage.getInput();
}
}